package org.common.utils.config;

import org.common.utils.config.annotation.ConfigClass;
import org.common.utils.config.annotation.ConfigKey;
import org.common.utils.config.handler.*;
import org.common.utils.json.JSONTool;
import org.common.utils.tool.ClassTool;
import org.common.utils.tool.StringTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 配置实例
 * @author zhouzhibing
 * @date 2025/7/29
 */
public class ConfigObject {

    private static final Logger log = LoggerFactory.getLogger(ConfigObject.class);

    /**
     * 配置实体类<类型，实体对象>
     */
    private Map<Class<?> , Object> entityMap;
    /**
     * 配置集合<配置key,配置value>
     */
    private Map<String , String> configMap;
    /**
     * 配置信息
     */
    private ConfigInfo configInfo;
    /**
     * 配置类包名地址
     */
    private String packageName;
    /**
     * 配置处理器列表
     */
    private List<AbstractConfigHandler> handlerList;

    public ConfigObject() {
        handlerList = List.of(new FileHandler(), new ResourceHandler(), new NacosHandler(), new EnvHandler());
    }

    /**
     * 根据配置信息加载配置
     * @param configInfo 配置信息
     * @param packageName 配置类包名地址
     */
    public synchronized void load(ConfigInfo configInfo, String packageName) {
        if (this.configInfo != null) {
            throw new ConfigException("请勿重复加载配置");
        }
        doLoad(configInfo, packageName);
    }

    /**
     * 重新加载配置
     */
    public void reLoad() {
        if(this.configInfo == null || this.packageName == null) {
            throw new ConfigException("请先加载配置");
        }
        doLoad(configInfo , packageName);
    }

    /**
     * 执行加载配置
     * @param configInfo 配置信息
     * @param packageName 包名
     */
    private void doLoad(ConfigInfo configInfo, String packageName) {
        this.configInfo = configInfo;
        this.packageName = packageName;

        Map<String , String> configMap = loadConfig(configInfo);
        Map<Class<?>, Object> entityMap = loadEntity(configMap, packageName);
        this.entityMap = entityMap;
        this.configMap = configMap;
    }

    /**
     * 根据配置类，获取配置
     * @param clzss 类
     * @return 配置实体类
     * @param <T> 泛型
     */
    public <T> T getConfig(Class<T> clzss) {
        if(entityMap == null) {
            throw new ConfigException("ConfigManager not init, please ConfigManager.load !");
        }

        if(clzss == null) {
            throw new ConfigException("clzss is null");
        }

        T t = (T) entityMap.get(clzss);
        if(t == null)
            throw new ConfigException("ConfigManager not exist config class , " + clzss.getName());

        return t;
    }

    /**
     * 根据key名，获取配置值。(不推荐使用，建议使用类配置获取)
     * @param key key名
     * @return 配置值
     */
    @Deprecated
    public String getConfig(String key) {
        return configMap.get(key);
    }

    /**
     * 加载配置
     * @param configInfo 配置信息
     * @return 加载的配置列表
     */
    private Map<String , String> loadConfig(ConfigInfo configInfo) {

        //加载所有配置处理器
        Map<String , String> configMap = new ConcurrentHashMap<>();
        for (AbstractConfigHandler configHandler : handlerList) {
            boolean success = configHandler.load(this , configInfo , configMap);
            log.info("ConfigManager.load success , configHandler = {} , success = {}" , configHandler.getClass() , success);
        }

        initReference(configMap);
        return configMap;
    }

    /**
     * 加载实体
     * @param configMap 配置列表
     * @param packageName 包地址
     * @return 实体列表
     */
    private Map<Class<?> , Object> loadEntity(Map<String , String> configMap , String packageName) {
        if (StringTool.isEmpty(packageName)) {
            ConfigException e = new ConfigException("ConfigManager.initEntity packageName is empty !");
            log.error("" , e);
            throw e;
        }

        Map<Class<?> , Object> entityMap = new ConcurrentHashMap<>();
        Class<?> currentClass = null;
        Field currentField = null;

        try {

            Set<Class<?>> aLlClass = ClassTool.getAllClass(packageName);
            for (Class<?> clzss : aLlClass) {
                if (!clzss.isAnnotationPresent(ConfigClass.class)) {
                    continue;
                }

                currentClass = clzss;
                Object o = clzss.getDeclaredConstructor().newInstance();

                List<Field> allFields = ClassTool.getAllFields(clzss);
                for (Field field : allFields) {
                    field.setAccessible(true);
                    currentField = field;

                    if (!field.isAnnotationPresent(ConfigKey.class)) {
                        continue;
                    }

                    ConfigKey configKey = field.getAnnotation(ConfigKey.class);
                    if(configKey == null)
                        continue;

                    String key = configKey.key();
                    if(StringTool.isEmpty(key)) {
                        throw new ConfigException("@ConfigKey key is null or empty. clzss " + clzss + "#" + field.getName());
                    }

                    String valueString = configMap.get(key);
                    if(valueString == null) {
                        valueString = configKey.value();
                    }

                    Object value = StringTool.toTypeValue(field.getType() , valueString);
                    field.set(o, value);
                }

                entityMap.put(clzss , o);
                log.info("ConfigManager.loadEntity success clzss = {}" , clzss.getName());
            }
        } catch (Exception e) {
            throw new ConfigException("currentField " + currentField , e);
        }
        return entityMap;
    }

    /**
     * 初始化配置中的引用值。
     */
    private void initReference(Map<String, String> propertieMap) {
        for (Map.Entry<String , String> entry : propertieMap.entrySet()) {
            String key = entry.getKey();
            String valueString = entry.getValue();

            int start = valueString.indexOf("${");
            if(start >= 0) {
                int end = valueString.indexOf("}");
                while(start >= 0)
                {
                    String refKey = valueString.substring(start + 2, end);
                    String refValue = propertieMap.get(refKey);
                    if(refValue == null) {
                        throw new ConfigException("ConfigManager.initReference , ref key not found: curKey = " + key +" , refKey = " + refKey);
//                        log.warn("ConfigManager.initReference ref key not found: curKey = {} , refKey = {} " , key , refKey);
//                        break;
                    }
                    valueString = valueString.replaceAll("\\$\\{"+refKey+"\\}", refValue);
                    propertieMap.put(key, valueString);

                    start = valueString.indexOf("${");
                    end = valueString.indexOf("}");
                }
            }
        }
    }

    @Override
    public String toString() {
        return JSONTool.toJSONString(this.configMap);
    }
}
